package com.nus.fellowmark.modules.user.service.impl;

import com.nus.fellowmark.common.exception.RRException;
import com.nus.fellowmark.common.response.ResponseEnum;
import com.nus.fellowmark.common.response.ResponseWrapper;
import com.nus.fellowmark.entity.UserEntity;
import com.nus.fellowmark.modules.rbac.enums.RoleEnum;
import com.nus.fellowmark.modules.rbac.service.SysUserRoleMappingService;
import com.nus.fellowmark.modules.user.dto.UserLoginDTO;
import com.nus.fellowmark.modules.user.dto.UserRegisterDTO;
import com.nus.fellowmark.modules.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.UUID;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nus.fellowmark.common.utils.PageUtils;
import com.nus.fellowmark.common.utils.Query;

import com.nus.fellowmark.dao.UserDao;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;


@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {

    @Autowired
    UserDao userDao;

    @Autowired
    SysUserRoleMappingService sysUserRoleMappingService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<UserEntity> page = this.page(
                new Query<UserEntity>().getPage(params),
                new QueryWrapper<UserEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * user register
     *
     * @param userRegisterDTO
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResponseWrapper<Integer> register(UserRegisterDTO userRegisterDTO) {
        String email = userRegisterDTO.getEmail();
        QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<UserEntity>().eq("email", email);
        UserEntity entity = userDao.selectOne(queryWrapper);
        // if email has been registered
        if (entity != null) {
            return new ResponseWrapper<>(ResponseEnum.EMAIL_DUPLICATE, null);
        }

        UserEntity user = new UserEntity();
        // MD5 encryption
        String salt = UUID.randomUUID().toString().toUpperCase();
        String password = getMd5Password(userRegisterDTO.getPassword(), salt);
        user.setPassword(password);
        user.setSalt(salt);
        user.setEmail(userRegisterDTO.getEmail());
        user.setName(userRegisterDTO.getName());

        boolean save = this.save(user);
        if (!save) {
            return new ResponseWrapper<>(ResponseEnum.REGISTER_FAIL, null);
        }
        // All user register as student first, then super-admin / admin can set user as admin/staff
        Boolean result = sysUserRoleMappingService.addUserAsRole(user.getId(), RoleEnum.STUDENT);
        if (!result) {
            throw new RRException(ResponseEnum.FAIL_TO_REGISTER.getMsg());
        }

        return new ResponseWrapper<>(ResponseEnum.OK, user.getId());
    }

    @Override
    public ResponseWrapper<Integer> checkPassword(UserLoginDTO userLoginDTO) {
        String email = userLoginDTO.getEmail();
        QueryWrapper<UserEntity> queryWrapper = new QueryWrapper<UserEntity>().eq("email", email);
        UserEntity entity = userDao.selectOne(queryWrapper);
        // if email not exist
        if (entity == null) {
            return new ResponseWrapper<>(ResponseEnum.ACCOUNT_NOT_REGISTER, null);
        }
        String salt = entity.getSalt();
        String md5Password = getMd5Password(userLoginDTO.getPassword(), salt);
        if (!md5Password.equals(entity.getPassword())) {
            return new ResponseWrapper<>(ResponseEnum.ACCOUNT_PASSWORD_NOT_MATCH, null);
        }
        return new ResponseWrapper<>(ResponseEnum.OK, entity.getId());
    }

    /**
     *
     * @param userIds
     * @return
     */
    @Override
    public void removeByUserIds(List<Integer> userIds) {
        boolean removeUsers = this.removeByIds(userIds);
        sysUserRoleMappingService.removeUserbyId(userIds);
    }

    @Override
    public String getUserName(Integer studentId) {

        return userDao.selectById(studentId).getName();
    }

    @Override
    public UserEntity getByStudentId(Integer studentId) {
        return userDao.selectById(studentId);
    }

    /**
     * password encryption:
     * 1. use UUID as salt value. Splice on the left and right sides of the original password
     * 2. Cycle encryption 3 times
     * @param password
     * @param salt
     * @return
     */
    private String getMd5Password(String password, String salt) {
        for (int i = 0; i < 3; i++) {
            password = DigestUtils.md5DigestAsHex((salt + password + salt).getBytes()).toUpperCase();
        }
        return password;
    }

    @Override
    public UserEntity getByEmail(String email) {
        return userDao.selectByEmail(email);
    }
}
